Parte 4: Aprendizado Federado usando Média de Modelos

Recapitulando: Na Parte 2 deste tutorial, nós treinamos um modelo usando uma versão bem simples do Aprendizado Federado. Isso exigia que cada proprietário dos dados confiasse no proprietário do modelo para poder ver seus gradientes.

Descrição: Neste tutorial, mostraremos como usar as ferramentas avançadas de agregação da Parte 3 para permitir que os pesos sejam agregados por um \"worker seguro\" confiável antes que o modelo resultante final seja enviado de volta ao proprietário do modelo (nesse caso, nós).

Dessa maneira, somente o worker seguro pode ver de quem são os pesos. Talvez possamos saber quais partes do modelo foram alteradas, mas NÃO saberemos qual worker (bob ou alice) fez tal alteração, o que cria uma camada de privacidade.

Autores:

Tradução:


In [ ]:
import torch
import syft as sy
import copy
hook = sy.TorchHook(torch)
from torch import nn, optim

Passo 1: Criar Proprietários de Dados

Primeiro, vamos criar dois proprietários de dados (Bob e Alice), cada um com alguns dados. Também vamos inicializar uma máquina segura chamada "secure_worker". Na prática, pode ser um hardware seguro (como o SGX da Intel) ou simplesmente um intermediário confiável.


In [ ]:
# criando alguns workers

bob = sy.VirtualWorker(hook, id="bob")
alice = sy.VirtualWorker(hook, id="alice")
secure_worker = sy.VirtualWorker(hook, id="secure_worker")


# Nosso Dataset de exemplo
data = torch.tensor([[0,0],[0,1],[1,0],[1,1.]], requires_grad=True)
target = torch.tensor([[0],[0],[1],[1.]], requires_grad=True)

# obtenha apontadores para os dados de treinamento de cada worker 
# enviando alguns dados de treinamento para bob e alice
bobs_data = data[0:2].send(bob)
bobs_target = target[0:2].send(bob)

alices_data = data[2:].send(alice)
alices_target = target[2:].send(alice)

Passo 2: Criar Nosso Modelo

Neste exemplo, vamos treinar com um modelo linear simples. Podemos inicializá-lo, normalmente, usando o construtor nn.Linear do PyTorch.


In [ ]:
# Inicialize o modelo
model = nn.Linear(2,1)

Passo 3: Envie uma Cópia do Modelo para Alice e Bob

Em seguida, precisamos enviar uma cópia do modelo atual para Alice e Bob para que eles possam executar as etapas de aprendizado em seus próprios conjuntos de dados.


In [ ]:
bobs_model = model.copy().send(bob)
alices_model = model.copy().send(alice)

bobs_opt = optim.SGD(params=bobs_model.parameters(),lr=0.1)
alices_opt = optim.SGD(params=alices_model.parameters(),lr=0.1)

In [ ]:

Passo 4: Treine os Modelos de Alice e Bob (em paralelo)

De forma convencional, na aprendizagem federada via Secure Averaging (Média Segura), cada proprietário dos dados primeiro treina seu modelo em várias iterações localmente antes que seja feito o cálculo da média dos modelos.


In [ ]:
for i in range(10):

    # Treina o Modelo de Bob
    bobs_opt.zero_grad()
    bobs_pred = bobs_model(bobs_data)
    bobs_loss = ((bobs_pred - bobs_target)**2).sum()
    bobs_loss.backward()

    bobs_opt.step()
    bobs_loss = bobs_loss.get().data

    # Treina o Modelo de Alice
    alices_opt.zero_grad()
    alices_pred = alices_model(alices_data)
    alices_loss = ((alices_pred - alices_target)**2).sum()
    alices_loss.backward()

    alices_opt.step()
    alices_loss = alices_loss.get().data
    
    print("Bob:" + str(bobs_loss) + " Alice:" + str(alices_loss))

Passo 5: Enviar Ambos os Modelos Atualizados para um Worker Seguro

Agora que cada proprietário de dados possui um modelo parcialmente treinado, é hora de calcular a média entre eles de maneira segura. Conseguimos isso instruindo Alice e Bob a enviar seus modelos para o servidor seguro (confiável).

Observe que esse uso de nossa API significa que cada modelo é enviado DIRETAMENTE ao secure_worker. Mas, nunca vemos isso.


In [ ]:
alices_model.move(secure_worker)

In [ ]:
bobs_model.move(secure_worker)

Passo 6: Calcular a Média dos Modelos

Finalmente, o último passo é calcular a média dos modelos treinados por Bob e Alice e, em seguida, usá-lo para definir os valores dos pesos do nosso "modelo" global.


In [ ]:
with torch.no_grad():
    model.weight.set_(((alices_model.weight.data + bobs_model.weight.data) / 2).get())
    model.bias.set_(((alices_model.bias.data + bobs_model.bias.data) / 2).get())

Apenas Repita!

E agora só precisamos repetir esse processo várias vezes!


In [ ]:
iterations = 10
worker_iters = 5

for a_iter in range(iterations):
    
    bobs_model = model.copy().send(bob)
    alices_model = model.copy().send(alice)

    bobs_opt = optim.SGD(params=bobs_model.parameters(),lr=0.1)
    alices_opt = optim.SGD(params=alices_model.parameters(),lr=0.1)

    for wi in range(worker_iters):

        # Treina o Modelo de Bob
        bobs_opt.zero_grad()
        bobs_pred = bobs_model(bobs_data)
        bobs_loss = ((bobs_pred - bobs_target)**2).sum()
        bobs_loss.backward()

        bobs_opt.step()
        bobs_loss = bobs_loss.get().data

        # Treina o Modelo de Alice
        alices_opt.zero_grad()
        alices_pred = alices_model(alices_data)
        alices_loss = ((alices_pred - alices_target)**2).sum()
        alices_loss.backward()

        alices_opt.step()
        alices_loss = alices_loss.get().data
    
    alices_model.move(secure_worker)
    bobs_model.move(secure_worker)
    with torch.no_grad():
        model.weight.set_(((alices_model.weight.data + bobs_model.weight.data) / 2).get())
        model.bias.set_(((alices_model.bias.data + bobs_model.bias.data) / 2).get())
    
    print("Bob:" + str(bobs_loss) + " Alice:" + str(alices_loss))

Por fim, queremos garantir que nosso modelo resultante tenha aprendido corretamente, para que possamos avaliá-lo em um conjunto de dados de teste. Neste problema simples, usamos os dados originais, mas, na prática, queremos usar novos dados para entender o quão generalizado o modelo é para exemplos não vistos.


In [ ]:
preds = model(data)
loss = ((preds - target) ** 2).sum()

In [ ]:
print(preds)
print(target)
print(loss.data)

Neste exemplo, o modelo médio resultante está pouco ajustado em relação a como se comportaria um modelo individual treinado localmente, no entanto, pudemos treiná-lo sem expor os dados de treinamento de cada worker. Também fomos capazes de agregar os modelos atualizados de cada worker em um lugar confiável para evitar vazamento de dados do proprietário do modelo.

Em um tutorial futuro, teremos como objetivo fazer nossa agregação segura e confiável diretamente com os gradientes, para que possamos atualizar o modelo com melhores estimativas de gradientes e chegar a um modelo mais forte.


In [ ]:

Parabéns!!! - Hora de se juntar a comunidade!

Parabéns por concluir esta etapa do tutorial! Se você gostou e gostaria de se juntar ao movimento em direção à proteção de privacidade, propriedade descentralizada e geração, demanda em cadeia, de dados em IA, você pode fazê-lo das seguintes maneiras!

Dê-nos uma estrela em nosso repo do PySyft no GitHub

A maneira mais fácil de ajudar nossa comunidade é adicionando uma estrela nos nossos repositórios! Isso ajuda a aumentar a conscientização sobre essas ferramentas legais que estamos construindo.

Junte-se ao Slack!

A melhor maneira de manter-se atualizado sobre os últimos avanços é se juntar à nossa comunidade! Você pode fazer isso preenchendo o formulário em http://slack.openmined.org

Contribua com o projeto!

A melhor maneira de contribuir para a nossa comunidade é se tornando um contribuidor do código! A qualquer momento, você pode acessar a página de Issues (problemas) do PySyft no GitHub e filtrar por "Projetos". Isso mostrará todas as etiquetas (tags) na parte superior, com uma visão geral de quais projetos você pode participar! Se você não deseja ingressar em um projeto, mas gostaria de codificar um pouco, também pode procurar mais mini-projetos "independentes" pesquisando problemas no GitHub marcados como "good first issue".

Doar

Se você não tem tempo para contribuir com nossa base de códigos, mas ainda deseja nos apoiar, também pode se tornar um Apoiador em nosso Open Collective. Todas as doações vão para hospedagem na web e outras despesas da comunidade, como hackathons e meetups!

Página do Open Collective do OpenMined


In [ ]: